/**
 * 数据适配器模块
 * 负责多源数据接入与标准化，支持各种数据格式转换为地图要素
 */

import { Feature } from 'ol';
import { Point, LineString, Polygon, Geometry } from 'ol/geom';
import { DataAdapterOptions, DataSourceType, Coordinate, FeatureData, LoadProgress } from '../types';
import { DataUtil } from './data';

/**
 * 数据适配器类
 * 提供统一的数据接入和转换接口
 */
export class DataAdapter {
  private options: DataAdapterOptions;

  /**
   * 构造函数
   * @param options 数据适配器配置
   */
  constructor(options: DataAdapterOptions) {
    this.options = {
      lngField: 'lng',
      latField: 'lat',
      addressField: 'address',
      geocodingConfig: {
        maxConcurrent: 5,
        apiKey: ''
      },
      ...options
    };
  }

  /**
   * 转换数据为要素数组
   * @param data 原始数据
   * @returns Promise<Feature[]> 转换后的要素数组
   */
  async transformData(data: any): Promise<Feature<Geometry>[]> {
    switch (this.options.type) {
      case DataSourceType.GEOJSON:
        return this.transformGeoJSON(data);
      case DataSourceType.CSV:
        return this.transformCSV(data);
      case DataSourceType.ADDRESS:
        return this.transformAddressData(data);
      case DataSourceType.SHAPEFILE:
        return this.transformShapefile(data);
      case DataSourceType.DATABASE:
      case DataSourceType.API:
        return this.transformCustomData(data);
      default:
        throw new Error(`Unsupported data type: ${this.options.type}`);
    }
  }

  /**
   * 转换GeoJSON数据
   * @param geoJson GeoJSON数据
   * @returns Feature[] 要素数组
   */
  private transformGeoJSON(geoJson: any): Feature<Geometry>[] {
    if (!geoJson || !geoJson.features) {
      return [];
    }

    const features: Feature<Geometry>[] = [];

    geoJson.features.forEach((featureJson: any) => {
      const { geometry, properties } = featureJson;
      let feature: Feature<Geometry> | null = null;

      switch (geometry.type) {
        case 'Point':
          feature = DataUtil.createPointFeature(
            geometry.coordinates as Coordinate,
            properties
          );
          break;
        case 'LineString':
          feature = DataUtil.createLineFeature(
            geometry.coordinates as Coordinate[],
            properties
          );
          break;
        case 'Polygon':
          feature = DataUtil.createPolygonFeature(
            geometry.coordinates[0] as Coordinate[],
            geometry.coordinates.slice(1) as Coordinate[][],
            properties
          );
          break;
      }

      if (feature) {
        features.push(feature);
      }
    });

    return features;
  }

  /**
   * 转换CSV数据
   * @param csvData CSV数据
   * @returns Promise<Feature[]> 要素数组
   */
  private async transformCSV(csvData: any[]): Promise<Feature<Geometry>[]> {
    const features: Feature<Geometry>[] = [];

    for (const row of csvData) {
      const lngField = this.options.lngField!;
      const latField = this.options.latField!;
      const addressField = this.options.addressField!;

      // 优先使用经纬度字段
      if (row[lngField] && row[latField]) {
        const coordinate: Coordinate = [
          parseFloat(row[lngField]),
          parseFloat(row[latField])
        ];
        if (!isNaN(coordinate[0]) && !isNaN(coordinate[1])) {
          const feature = DataUtil.createPointFeature(coordinate, row);
          features.push(feature);
        }
      }
      // 否则尝试使用地址进行地理编码
      else if (row[addressField]) {
        try {
          const coordinate = await this.geocodeAddress(row[addressField]);
          if (coordinate) {
            const feature = DataUtil.createPointFeature(coordinate, row);
            features.push(feature);
          }
        } catch (error) {
          console.warn(`Geocoding failed for address: ${row[addressField]}`, error);
        }
      }
    }

    return features;
  }

  /**
   * 转换地址数据
   * @param addressData 地址数据数组
   * @returns Promise<Feature[]> 要素数组
   */
  private async transformAddressData(addressData: any[]): Promise<Feature<Geometry>[]> {
    const features: Feature<Geometry>[] = [];
    const addressField = this.options.addressField!;
    const concurrency = this.options.geocodingConfig?.maxConcurrent || 5;

    // 批量并发处理地址编码
    const batches: any[][] = [];
    for (let i = 0; i < addressData.length; i += concurrency) {
      batches.push(addressData.slice(i, i + concurrency));
    }

    for (const batch of batches) {
      const batchPromises = batch.map(async (item) => {
        const address = typeof item === 'string' ? item : item[addressField];
        if (!address) return null;

        try {
          const coordinate = await this.geocodeAddress(address);
          if (coordinate) {
            const properties = typeof item === 'string' ? { address } : item;
            return DataUtil.createPointFeature(coordinate, properties);
          }
        } catch (error) {
          console.warn(`Geocoding failed for address: ${address}`, error);
        }
        return null;
      });

      const batchResults = await Promise.all(batchPromises);
      // 修复filter类型守卫问题
      const validFeatures = batchResults.filter(feature => feature !== null) as Feature<Geometry>[];
      features.push(...validFeatures);
    }

    return features;
  }

  /**
   * 转换Shapefile数据（简化实现，实际项目可能需要使用第三方库）
   * @param shapefileData Shapefile数据
   * @returns Feature[] 要素数组
   */
  private transformShapefile(shapefileData: any): Feature<Geometry>[] {
    // 这里是简化实现，实际项目中可能需要使用如shapefile.js等库
    console.warn('Shapefile transformation is not fully implemented');
    return [];
  }

  /**
   * 转换自定义数据（数据库或API返回的数据）
   * @param customData 自定义数据
   * @returns Feature[] 要素数组
   */
  private transformCustomData(customData: any[]): Feature<Geometry>[] {
    const features: Feature<Geometry>[] = [];
    const lngField = this.options.lngField!;
    const latField = this.options.latField!;

    for (const item of customData) {
      if (item[lngField] && item[latField]) {
        const coordinate: Coordinate = [
          parseFloat(item[lngField]),
          parseFloat(item[latField])
        ];
        if (!isNaN(coordinate[0]) && !isNaN(coordinate[1])) {
          const feature = DataUtil.createPointFeature(coordinate, item);
          features.push(feature);
        }
      }
    }

    return features;
  }

  /**
   * 地址地理编码
   * @param address 地址文本
   * @returns Promise<Coordinate | null> 坐标或null
   */
  private async geocodeAddress(address: string): Promise<Coordinate | null> {
    try {
      // 这里是简化实现，实际项目中应使用真实的地理编码服务
      // 例如高德地图API、百度地图API等
      console.log(`Geocoding address: ${address}`);
      
      // 模拟地理编码结果
      // 实际项目中应替换为真实的API调用
      return [116.397428 + Math.random() * 0.1 - 0.05, 39.90923 + Math.random() * 0.1 - 0.05];
    } catch (error) {
      console.error('Geocoding error:', error);
      return null;
    }
  }

  /**
   * 批量导入数据
   * @param data 原始数据
   * @param options 批量导入选项
   * @param onProgress 进度回调
   * @returns Promise<Feature[]> 转换后的要素数组
   */
  async batchImport(
    data: any[],
    options: { batchSize?: number; concurrency?: number; retryCount?: number } = {},
    onProgress?: (progress: LoadProgress) => void
  ): Promise<Feature<Geometry>[]> {
    const batchSize = options.batchSize || 100;
    const concurrency = options.concurrency || 3;
    const retryCount = options.retryCount || 3;

    const total = data.length;
    const features: Feature<Geometry>[] = [];
    let loaded = 0;

    // 将数据分成多个批次
    const batches: any[][] = [];
    for (let i = 0; i < total; i += batchSize) {
      batches.push(data.slice(i, i + batchSize));
    }

    // 并发处理批次
    const batchPromises = batches.map(async (batch, batchIndex) => {
      let attempt = 0;
      let batchFeatures: Feature<Geometry>[] = [];

      while (attempt <= retryCount) {
        try {
          batchFeatures = await this.transformData(batch);
          break; // 成功则跳出循环
        } catch (error) {
          attempt++;
          console.warn(`Batch ${batchIndex} failed, attempt ${attempt}/${retryCount}`, error);
          if (attempt > retryCount) {
            console.error(`Batch ${batchIndex} failed after ${retryCount} attempts`);
          } else {
            // 重试前等待一段时间
            await new Promise(resolve => setTimeout(resolve, 1000 * attempt));
          }
        }
      }

      // 更新进度
      loaded += batch.length;
      const percent = Math.round((loaded / total) * 100);
      if (onProgress) {
        onProgress({
          loaded,
          total,
          percent,
          completed: loaded >= total
        });
      }

      return batchFeatures;
    });

    // 限制并发数
    const results: Feature<Geometry>[][] = [];
    for (let i = 0; i < batchPromises.length; i += concurrency) {
      const chunk = batchPromises.slice(i, i + concurrency);
      const chunkResults = await Promise.all(chunk);
      results.push(...chunkResults);
    }

    // 合并所有结果
    results.forEach(batchFeatures => {
      features.push(...batchFeatures);
    });

    return features;
  }
}